import { tBool, tNull, tNum, tStr, TypesInherent, TypesReceiveLiterial } from "."
import { oProject } from "../Project"
import { GetAllBundles } from "../pyBundles"
import { InterfaceType } from "./interface_type"
import { ListType } from "./list_type"
import { DataType } from "./type"
import { UnionType } from "./union_type"

export function receiveLiterial(t: DataType) {
  return TypesReceiveLiterial.map(x => x.name).includes(t.name)
}


export function makeOptional(t: DataType) {
  return new UnionType([t, tNull])
}


interface InterfaceTypeJsInit {
  __meta_name?: string
  [key: string]: any
}

export function typeFromJs(obj: InterfaceTypeJsInit): DataType {
  if (obj instanceof DataType) {
    return obj
  }
  else if (obj === Number) {
    return tNum
  }
  else if (obj === String) {
    return tStr
  }
  else if (obj === Boolean) {
    return tBool
  }
  else if (Object.prototype.toString.call(obj) === '[object Object]') {
    const member = Object.keys(obj).map(k => {
      return k.startsWith('__meta') ? undefined : {
        name: k,
        type: typeFromJs(obj[k])
      }
    })
    const name = obj['__meta'] ?? ''
    return new InterfaceType(name, member.filter(x => !!x))
  }
  else {
    console.error(obj)
    throw new Error(`无法基于object转换为类型`)
  }
}

export function getTypesFromModules(): DataType[] {
  return lodash.flatten(GetAllBundles()
    .filter(m => oProject.value!.modulesEnabled.includes(m.name))
    .map(m => m.typeDeclares))
}
export function getAllExplicitTypes() {
  return [
    ...TypesInherent,
    ...getTypesFromModules()
  ].filter(t => {
    return !t.name.includes('any') && !t.name.includes('|')
  })
}

export function typeOfValue(v: any): DataType {
  for (const paraType of getAllExplicitTypes()) {
    if (paraType.receiveValue(v)) {
      return v
    }
  }
  console.error(v)
  throw new Error('没有为值匹配到类型')
}

export function explicitTypeOf(t: DataType): DataType {
  if (t instanceof ListType) {
    for (const paraType of getAllExplicitTypes()) {
      if (paraType.receiveType(t.itemType)) {
        return new ListType(t.itemType)
      }
    }
  }
  for (const paraType of getAllExplicitTypes()) {
    if (paraType.receiveType(t)) {
      return t
    }
  }
  console.error(t)
  throw new Error('没有为类型找到精确类')
}